/**
 * The contents of this file are subject to the license and copyright detailed in the LICENSE and NOTICE files at the root of the source tree and available online at
 * http://www.askosi.org/license.html
 */
package be.destin.skos.html;

import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;

import be.destin.skos.core.Concept;
import be.destin.skos.core.ConceptScheme;
import be.destin.skos.core.Count;
import be.destin.skos.core.CountList;
import be.destin.skos.core.Profile;
import be.destin.skos.core.ReferringApplication;
import be.destin.skos.core.SchemeInterface;
import be.destin.skos.core.SchemeUsage;
import be.destin.skos.core.SchemeUsageList;
import be.destin.skos.view.SkosView;
import be.destin.skos.view.SkosViewType;

public class SkosHtmlTable {

	private SkosHtmlManager skosHtmlManager;
	private Profile currProfile;
	private LinkedHashMap<String,LinkedHashSet<String>> tableUsages = new LinkedHashMap<String,LinkedHashSet<String>>();
	private LinkedList<ConceptScheme> involvedSchemes = new LinkedList<ConceptScheme>();
	private boolean rowEven = true;
	private boolean first = true;
	private boolean hierarchized = false;
	private boolean basketEnabled = false; // Does the JavaScript to manage skosBasket is available?
	private SkosViewType viewType = SkosViewType.ALFA;
	private int limit = Integer.MAX_VALUE;
	private long[] totals;
    private boolean colSpanning = false; // New format to draw vertical lines between a "father" concept and its children

	private boolean categorized = true;
	
	private static final String appSepStyle = " style=\"border-left:1px solid black\"";
	private static final String skosBasketColumn = "basket";
	
	public SkosHtmlTable(SkosHtmlManager skosHtmlManager, SkosView currentView) {
		super();
		this.skosHtmlManager = skosHtmlManager;
		this.currProfile = skosHtmlManager.getProfileData();
		this.viewType = currentView.getType();
		this.basketEnabled = currentView.isBasket();
	}

	public SkosHtmlTable(SkosHtmlManager skosHtmlManager) {
		super();
		this.skosHtmlManager = skosHtmlManager;
		this.currProfile = skosHtmlManager.getProfileData();
	}

	public int putSchemes (LinkedList<ConceptScheme> schemes) {
		int nbColumns = 0;
		for (ConceptScheme aScheme : schemes) {
			nbColumns += putScheme(aScheme);
		}
		return nbColumns;
	}

	public synchronized int putScheme (ConceptScheme aScheme) {
		int nbColumns = 0;
		if (!involvedSchemes.contains(aScheme)) {
			involvedSchemes.add(aScheme);
			if (aScheme.isHierarchized()) {
				hierarchized = true;
			}
			LinkedList<String> applicationList = new LinkedList<String>();
			ConceptScheme definedApplications = skosHtmlManager.getSkosManager().getApplications();
			SchemeUsageList usages = aScheme.getUsage();
			if (usages != null) {
				if (definedApplications == null) {
					for (SchemeUsage anUsage : usages) {
						String anApplicationKey = anUsage.getApplication();
						if (!applicationList.contains(anApplicationKey)) {
							applicationList.add(anApplicationKey);
						}
					}
				} else {
					for (Concept anApplication : definedApplications.getConcept()) {
						String applicationAbout = anApplication.getAbout();
						if (!applicationList.contains(applicationAbout)) {
							applicationList.add(applicationAbout);
						}
					}
				}
				if ( ! applicationList.isEmpty()) {
					for (String applicationAbout : applicationList) {
						Collection<SchemeUsage> applicationUsages = usages.getApplicationUsage(applicationAbout);
						if (applicationUsages != null && ! applicationUsages.isEmpty()) {
							LinkedHashSet<String> applicationRoles = tableUsages.get(applicationAbout);
							if (applicationRoles == null) applicationRoles = new LinkedHashSet<String>();
							for (SchemeUsage anUsage : applicationUsages) {
								if ( ! applicationRoles.contains(anUsage.getRole())) {
									applicationRoles.add(anUsage.getRole());
									nbColumns++;
								}
							}
							if ( ! applicationRoles.isEmpty()) {
								tableUsages.put(applicationAbout,applicationRoles);
							}
						}
					}
				}
			}
		}
		return nbColumns;
	}

	public String toHtmlColGroups(){
		StringBuffer out = new StringBuffer();
		if (isColSpanning()) {
			for (int i=0; i <= Concept.maxDepth; i++) {
				out.append("<col/>");				
			}
		}
		if (isBasket()) {
			out.append("<col/>");
		}
		boolean odd = true;
		LinkedList<String> applicationList = null;
		if (currProfile != null) {
			applicationList = currProfile.getDefinedApplicationsList();
			if (applicationList != null && !applicationList.isEmpty()) {
				LinkedList<String> roleList = currProfile.getDefinedRolesList();
				for (String aboutApplication : applicationList) {
					if (tableUsages.containsKey(aboutApplication)){
						LinkedHashSet<String> applicationEntry = tableUsages.get(aboutApplication);
						boolean first = true;
						for (String aRole : roleList) {
							if (applicationEntry.contains(aRole)) {
								//out.append("<col class=\"SkosTableColumn"+aRole+"\"/>");
								out.append("<col class=\"SkosTableColumn"+(odd?"Odd":"Even")+(first?"First":"")+"\"/>");
								odd = !odd;
								first = false;
							}
						}
					}
				}
			}
		}
		return out.toString();
	}

	public String toHtmlApplicationHeaders(){
		StringBuffer out = new StringBuffer();
		if (this.isBasket()) {
			out.append("<th class=\"SkosTableApplication\"> &nbsp; </th>");
			
		}
		if (currProfile != null) {
			LinkedList<String> applicationList = currProfile.getDefinedApplicationsList();
			if (applicationList != null && !applicationList.isEmpty()) {
				LinkedList<String> roleList = currProfile.getDefinedRolesList();
				for (String aboutApplication : applicationList) {
					if (tableUsages.containsKey(aboutApplication)){
						LinkedHashSet<String> applicationEntry = tableUsages.get(aboutApplication);
						int i = 0;
						for (String aRole : roleList) {
							if (applicationEntry.contains(aRole)) {
								i++;
							}
						}
						if (i > 0) {
							out.append("<th class=\"SkosTableApplication\" colspan=\"");
							out.append(i);
							out.append("\">");
							out.append(skosHtmlManager.toHtmlApplication(aboutApplication, null));
							out.append("</th>");
						}
					}
				}
			}
		}
		return out.toString();
	}

	public String toHtmlRoleHeaders(){
		StringBuffer out = new StringBuffer();
		if (this.isBasket()) {
			out.append("<th class=\"SkosTableRole\"");
			out.append(appSepStyle);
			out.append("><img");
			out.append(skosHtmlManager.askosiIcon(skosBasketColumn));
			out.append(" border=\"0\"/></th>");
		}
		int col = 0;
		if (currProfile != null) {
			LinkedList<String> applicationList = currProfile.getDefinedApplicationsList();
			LinkedList<String> roleList = currProfile.getDefinedRolesList();
			if (applicationList != null) {
				for (String aboutApplication : applicationList) {
					if (tableUsages.containsKey(aboutApplication)){
						LinkedHashSet<String> applicationEntry = tableUsages.get(aboutApplication);
						//int i = applicationEntry.size();
						int i = 0;
						for (String aRole : roleList) {
							if (applicationEntry.contains(aRole)) {
								col++;
								out.append("<th class=\"SkosTableRole\"");
								//i--;
								if (i==0) {
									out.append(appSepStyle);
								}
								i++;
								out.append(">");
								out.append(skosHtmlManager.toHtmlRole(aboutApplication, aRole, null));
								out.append("</th>");
							}
						}
					}
				}
			}
		}
		totals = new long[col];
		return out.toString();
	}
	
	public String toHtmlColumns(Concept aConcept){
		StringBuffer out = new StringBuffer();
		if (this.isBasket()) {
			out.append("<td align=\"center\"");
			out.append(appSepStyle);
			out.append(">");
			if (aConcept != null) {
				out.append(aConcept.toHtmlBasket(skosHtmlManager));
			}
			out.append("</td>");
		}
		int colCount = 0;
		if (currProfile != null) {
			LinkedList<String> applicationList = currProfile.getDefinedApplicationsList();
			if (applicationList != null && !applicationList.isEmpty()) {
				LinkedList<String> roleList = currProfile.getDefinedRolesList();
				for (String aboutApplication : applicationList) {
					if (tableUsages.containsKey(aboutApplication)){
						LinkedHashSet<String> applicationEntry = tableUsages.get(aboutApplication);
						ReferringApplication anApplicationUsage = null;
						if (aConcept != null) {
							anApplicationUsage = aConcept.getApplicationUsage(aboutApplication);
						}
						//int i = applicationEntry.size();
						int i=0;
						for (String aRole : roleList) {
							if (applicationEntry.contains(aRole)) {
								out.append("<td");
								//i--;
								if (i==0) {
									out.append(appSepStyle);
								}
								i++;
								out.append(">");
								Count count = null;
								if (anApplicationUsage != null) {
									CountList countList = anApplicationUsage.getCount();
									if (countList != null) {
										count = countList.getIndex(aRole);
									}
								}
								if (count == null || count.getCount() <= 0) {
									out.append("&nbsp;");
								} else {
									out.append(aConcept.toHtmlCount(skosHtmlManager, aboutApplication, count, categorized));
									totals[colCount] += count.getCount();
								}
								out.append("</td>");
								colCount++;
							}
						}
					}
				}
			}
		}
		return out.toString();
	}

	// Statistics of references for whole Schemes
	public String toHtmlColumns(SchemeInterface aScheme){
		StringBuffer out = new StringBuffer();
		if (!(aScheme instanceof ConceptScheme)) {
			return "";
		} else {
			SchemeUsageList usages = ((ConceptScheme)aScheme).getUsage();
			int colCount = 0;
			if (currProfile != null) {
				LinkedList<String> applicationList = currProfile.getDefinedApplicationsList();
				if (applicationList != null && !applicationList.isEmpty()) {
					LinkedList<String> roleList = currProfile.getDefinedRolesList();
					for (String aboutApplication : applicationList) {
						if (tableUsages.containsKey(aboutApplication)){
							LinkedHashSet<String> applicationEntry = tableUsages.get(aboutApplication);
							Collection<SchemeUsage> appUsages = usages.getApplicationUsage(aboutApplication);
							//int i = applicationEntry.size();
							int i=0;
							for (String aRole : roleList) {
								if (applicationEntry.contains(aRole)) {
									out.append("<td");
									//i--;
									if (i==0) {
										out.append(appSepStyle);
									}
									i++;
									out.append(">");
									SchemeUsage dataRole = null;
									if (appUsages != null) {
										Iterator<SchemeUsage> itUsage = appUsages.iterator();
										while (itUsage.hasNext()) {
											SchemeUsage anUsage = itUsage.next();
											if (anUsage.getRole().equals(aRole)) {
												dataRole = anUsage;
												break;
											}
										}
									}
									if (dataRole == null || dataRole.getTotal() <= 0) {
										out.append("&nbsp;");
									} else {
										out.append("<a href=\"view.jsp?scheme=");
										out.append(aScheme.getAbout());
										out.append("&display=tops&application=");
										out.append(aboutApplication);
										out.append("&role=");
										out.append(dataRole.getRole());
										if(skosHtmlManager.isFramedInAskosi()){
											out.append("&framed=yes");
										}
										out.append("#tops\"");
										if(skosHtmlManager.isFramedInAskosi()){
											out.append(" target=\"");
											out.append(SkosHtmlManager.askosiDisplayApplication);
											out.append("_scheme\"");
										}
										out.append('>');
										out.append(dataRole.getTotal());
										totals[colCount] += dataRole.getTotal();
										out.append("</a>");
										if (dataRole.isPropagated()) {
											out.append("<img"
													+ skosHtmlManager.askosiIcon("narrower_transitive")
													+ " border=\"0\"/>");
										}
									}
									colCount++;
									out.append("</td>");
								}
							}
						}
					}
				}
			}
			return out.toString();
		}
	}

	// Statistics of references for whole Schemes
	public String toHtmlTotalColumns(){
		StringBuffer out = new StringBuffer();
		int colCount = 0;
		if (currProfile != null) {
			LinkedList<String> applicationList = currProfile.getDefinedApplicationsList();
			if (applicationList != null && !applicationList.isEmpty()) {
				LinkedList<String> roleList = currProfile.getDefinedRolesList();
				for (String aboutApplication : applicationList) {
					if (tableUsages.containsKey(aboutApplication)){
						LinkedHashSet<String> applicationEntry = tableUsages.get(aboutApplication);
						int i=0;
						for (String aRole : roleList) {
							if (applicationEntry.contains(aRole)) {
								out.append("<th");
								//i--;
								if (i==0) {
									out.append(appSepStyle);
								}
								i++;
								out.append(" align=\"right\">");
								out.append(totals[colCount]);
								out.append("</th>");
								colCount++;
							}
						}
					}
				}
			}
		}
		return out.toString();
	}
	
	// Statistics of references for whole Schemes
	public String toHtmlSelectColumns(){
		StringBuffer out = new StringBuffer();
		if (currProfile != null) {
			LinkedList<String> applicationList = currProfile.getDefinedApplicationsList();
			if (applicationList != null && !applicationList.isEmpty()) {
				LinkedList<String> roleList = currProfile.getDefinedRolesList();
				for (String aboutApplication : applicationList) {
					if (tableUsages.containsKey(aboutApplication)){
						LinkedHashSet<String> applicationEntry = tableUsages.get(aboutApplication);
						int i=0;
						for (String aRole : roleList) {
							if (applicationEntry.contains(aRole)) {
								out.append("<th");
								//i--;
								if (i==0) {
									out.append(appSepStyle);
								}
								i++;
								out.append("><input type=\"radio\" name=\"focus\" value=\"");
								out.append(aboutApplication);
								out.append('_');
								out.append(aRole);
								out.append( "\"/></th>");
							}
						}
					}
				}
			}
		}
		return out.toString();
	}
	
	/**
	 * @return the tableUsages
	 */
	public LinkedHashMap<String, LinkedHashSet<String>> getTableUsages() {
		return tableUsages;
	}

	/**
	 * @return the involvedSchemes
	 */
	public LinkedList<ConceptScheme> getInvolvedSchemes() {
		return involvedSchemes;
	}

	public String nextRowClass() {
		rowEven = ! rowEven;
		if (first) {
			first = ! rowEven;
			return (rowEven?"SkosTableSecondRow":"SkosTableFirstRow");
		} else {
			return "SkosTable"+(rowEven?"Even":"Odd")+"Row";			
		}
	}

	public void initRow() {
		rowEven = true;
		first = true;
	}

	/**
	 * @return the skosHtmlManager
	 */
	public SkosHtmlManager getSkosHtmlManager() {
		return skosHtmlManager;
	}

	/**
	 * @return the hierarchized
	 */
	public boolean isHierarchized() {
		return this.hierarchized && this.viewType==SkosViewType.TREE;
	}

	/**
	 * @return the viewType
	 */
	public SkosViewType getViewType() {
		return viewType;
	}

	/**
	 * @param viewType the viewType to set
	 */
	public void setViewType(SkosViewType viewType) {
		this.viewType = viewType;
	}

	public int getLimit() {
		return limit;
	}

	public void setLimit(int limit) {
		this.limit = limit;
	}

    public boolean isColSpanning() {
		return colSpanning;
	}

    public String toHtmlWholeSpan() {
    	if (colSpanning) return " colspan=\""+(Concept.maxDepth+1)+"\"";
    	else return "";
	}

	public void setColSpanning(boolean colSpanning) {
		this.colSpanning = colSpanning;
	}

	public boolean isCategorized() {
		return categorized;
	}

	public void setCategorized(boolean categorized) {
		this.categorized = categorized;
	}

	public boolean isBasket() {
		return basketEnabled;
	}

	public void setBasket(boolean basketEnabled) {
		this.basketEnabled = basketEnabled;
	}

	/**
	 * @return the totals
	 */
	public long[] getTotals() {
		return totals;
	}
}
